package org.hyh.core.config.securityConfig;

import org.hyh.core.security.Filter.CustomUsernamePasswordAuthenticationFilter;
import org.hyh.core.security.Filter.JwtTokenValidateFilter;
import org.hyh.core.security.handler.HandlerWhenLoginSuccess;
import org.hyh.core.security.handler.JwtConfigurer;
import org.hyh.core.security.handler.handlerWhenLoginFail;
import org.hyh.core.security.handler.handlerWhenNoAuth;
import org.hyh.core.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

/**
 * @auther :hyh
 * @desc :
 * @date :2020/7/11
 */
  //HttpserveletResponse#resetBuff()      //重置缓冲区的数据，不重置 返回状态码和 相应头headers
  //HttpserveletResponse#isCommitted()   //判断是否提交

/**
 *  UserDetails :  认证登录 成功后返回的对象，  这个对象 包含 该用户的权限。 一般我们都会 继承该对象。
 *  Authentication: 认证对象，常用的实现类是：UsernamePasswordAuthenticationToken， 封装了username/password
 *
 *
 *  登录的主要流程：   UsernamePasswordAuthenticationFilter 中的doFilter() 方法：
 *   1. 验证当前请求是否是登录请求，不是执行doFilter。
 *   2. 尝试登录，从request 中获取username/password封装成UsernamePasswordAuthenticationToken对象
 *      在由AuthenticationManager对象执行authenticate（）方法。
 *      ps:这里AuthennticationManager的实现是  ProviderManager
 *
 *   3. ProviderManager获取所有的AuthenticationProvider对象，遍历。 找到支持的authenticate（）的对象。
 *
 *   4. 一般使用DaoAuthenticationProvider对象， 该对象会调用retrieveUser（）---》getUserDetailsService().loadUserByUsername(username)
 *      从而实现登录。
 *   5. 登录成功/失败  默认都会进行跳转到设置的页面。重写可实现，对应的handler可以写入json ，从而实现处理ajax请求。
 *

 *
 *
 */





/**                          在FilterComparator 类中可以看见 常见过滤器的  【位置顺序】
 * 1.  内置过滤器的初始化：    在创建httpSecurity 对象时，FilterComparator 回对其排序。
 *
 * 2.  AutowireBeanFactoryObjectPostProcessor#ObjectPostProcessor () 手动注册一个new 的对象到springIOC容器中。
 *
 *
 *
 * 2.  JWT的整合
 *        1. 登录成功后，在 successLoginHandler 生成token 返回。 在 failLoginHandler 提供失败的响应信息。
 *        2. 非登录的接口的jwt认证   需要自己 实现OncePerRequestFilter 类，  该类注册时， 必须 放在UsernamePasswordAuthenticationFilter 之前。
 *        4. formLogin 的登录，直接使用UsernamePasswordAuthenticationFilter接口即可。
 *           使用json 的方式的登录，可以继承  AbstractAuthenticationProcessingFilter类，重写attemptAuthentication（）方法，
 *            将读取数据的方式变为 读取requestBody 的方式。
 *
 *  3. 图形验证码校验：
 *        1. 自定义实现一个Filter ,  如果 判断拦截登录请求， 判断验证码，如果不成功就直接 抛出认证异常。
 *        2. 一般使用DaoAuthenticationProvider对象来实现 登录，  在authenticate（）时，会进行各种check,
 *           所以我们一般实现自己的Provider ,重写一个check 方法，在里面加入验证码的逻辑。
 *
 *
 * 3. springSecurity 的 同意异常处理：   springSecurity的异常分类两大类：
 *        1. AuthenticationException  认证相关的异常               //  实现AuthenticationEntryPoint  来处理异常
 *        2. AccessDeniedException   访问受保护资源抛出的异常       //  实现AccessDeniedHandler  来处理异常。
 *
 * 4.  接口访问权限的：
 *       1. 在 WebSecurityConfigurerAdapter的实现类中进行配置。
 *       2. 使用@EnableGlobalMethodSecurity 开启注解方式，  有三种可以用的方法，默认均为false
 *
 *  5.  SecurityContext : 表示一个和当前线程有关的 安全信息 对象。
 *      SecurityContextHodler:  定义了一系列的静态方法，增删改和当前线程挂有关的SucurityContext对象。
 *
 *  6. Spring Security 以一个单 Filter（FilterChainProxy） 存在于整个过滤器链中，
 *       而这个 FilterChainProxy 实际内部代理着众多的 Spring Security Filter
 *
 *  7. 一般来说，springSecurity不同的Filter 都对应着  一个 SecurityConfigurer对象。
 *    webSecurity对象： 包含了我们自定义的信息 的信息，最终该对象 的信息会被封装成一个  filterChain。
 *
 *    springSercurity 收集了所有的SecurityConfigurer对象并放入    【webSecurity对象】中。
 *    在webSecurity对象  执行doBuild() 构建springSecurityFilterChain 会遍历   SecurityConfigurer集合，
 *    执行对应的方法。
 *
 *  8. GenericFilterBean 类： springWeb提供的 Filter,用于springMVC环境中的Filter
 *
 *  9. Spring提供了 一个GenericFilterBean 类的实现 【DelegatingFilterProxy过滤器】，
 *      代理过滤器  加入 javaWeb环境中的FilterChain;   而 springSecurityFilter  由代理过滤器执行。
 *
 *  10. 通过配置maximumSessions(1).sessionRegistry(sessionRegistry())   来保证同一时间只有一个用户，   JWT 不需要
 *      并且可以自定义一个 sessionRegistry 来设置session的保存位置。
 *      还需要一个注入HttpSessionEventPublisher 对象，因为springSecurity 中通过监听Session的销毁事件， 来清理session
 *
 *
 *
 *
 *
 *
 */




  //基于 redisSession 和 JWT 的spring-security 的整合。

@Configuration
public class FormLoginConfig  extends WebSecurityConfigurerAdapter {

    private static  String loginUrl= "/admin/login";
    @Autowired
    UserService userService;


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //自定义的filter,尽量不要交给 spring 管理防止，filter 多次执行的情况出现。
        AntPathRequestMatcher requestMatcher = new AntPathRequestMatcher(loginUrl, "POST");
        JwtTokenValidateFilter jwtTokenValidateFilter = new JwtTokenValidateFilter(requestMatcher, userService);

        CustomUsernamePasswordAuthenticationFilter customloginFilter = new CustomUsernamePasswordAuthenticationFilter(loginUrl, super.authenticationManager(),
                new HandlerWhenLoginSuccess(), new handlerWhenLoginFail());

        http.csrf().disable().cors()
                .and()
                .addFilterBefore(customloginFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(jwtTokenValidateFilter, CustomUsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(new handlerWhenNoAuth())
                .and()
                .formLogin()
                .loginProcessingUrl(loginUrl)
                .successHandler(new HandlerWhenLoginSuccess())
                .failureHandler(new handlerWhenLoginFail())
                .and()
                .authorizeRequests()
                .antMatchers("/train/train-user").hasAnyRole("admin","user")
                .antMatchers("/train/train-admin").hasRole("admin")
                .antMatchers("/train/all").permitAll();

                //todo 配置。
    }


    @Override
    public void configure(WebSecurity web) throws Exception {
        //静态资源不需要走，springSecurity的filterChain
        web.ignoring().mvcMatchers(HttpMethod.OPTIONS, "/**")
                .mvcMatchers("/**/*.{js,html,css}")
                .antMatchers("/webjars/**");
    }





    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    @ConditionalOnMissingBean
    public AuthenticationEntryPoint authenticationEntryPoint(){
         return  new handlerWhenNoAuth();
    }



}
